VBcrackme 解析手引き「VBCrack2」


−講義に入る前に−

今回から3回ほど、WKTVBDEを使ってP-Codeプログラムの解析説明を行います。
私も、WKTVBDE と P-Codeには詳しくないので、誤爆する可能性が有ります。
説明途中に出てくるP-Codeの動作説明等 誤爆してたら、訂正意見お願いします。

では、今回から使うソフトを用意してインストールを行って下さい。

・WKTVBDebugger(以後、WKTVBDEと呼称)
・ExDec (VB DeComplier)
・Visual Basic 6.0 SP5 ランタイム(VBランタイムが未インストールの場合)

WKTVBDEですが、インストールの際、環境によってインストールが出来ない場合があります。
セットアップ時にImageHelp.dllがどうたらというエラーが出ても「無視」を押せばいいです。
WKTVBDE.DLLとbdasmdll.dllをWINDIR%system32にコピーしておきましょう。

それでもエラーが出て使えないような場合は、imagehlp.DLLをsystem32フォルダから取り出して、
どこかに移動させておいて下さい。
それからインストールするとシステムフォルダに新しく「imagehlp.dll」が
出来て、正常にインストールが完了し、使えるようになると思います。
その際もWKTVBDE.DLLとbdasmdll.dllはWINDIR%system32にコピーするのを忘れないで下さい。


最初に、補助ツール ExDec (VB DeComplier)を起動します。 「&File」→「&Disasemble Prog」を選択し、VBCrack2.exe を、読込みます。 P-Codeのリストが表示されたと思います。 これで、補助ツール ExDec は終了させます。 先程、表示されてたP-Codeのリストは、VBCrack2.exe と同じディレクトリの下に、「my.txt」と いうファイルで作成されてます。 エディタ等で、my.txt のファイルを開いて見れば、WKTVBDEのP-Codeリスト表示範囲の狭い欠点を 補えると思います。(個人的な意見です。) さて、いよいよ WKTVBDE を起動します。 「P-Code Loader xxx」なるウィンドウが表示されます。 「&File」→「&Open」又は、ツールバーの「Open File」を選択しVBCrack2.exe を、読込みます。 ログ表示部分(右側)に数行表示されました。 一番最後の表示が、「File loaded sucessfully press the RUN button to run the debugger.」ですね。 これからの説明は、タイミングが重要なので全部読み終ってから、PCの操作お願いします。 「&Action」→「&Run」又は、ツールバーの「Run」を選択します。 Information のダイアログが2回表示されますが、素早く「OK」ボタンクリックしてください。 この時に、Information のダイアログのOKボタンを押さないでいると、WKTVBDEが自動で終了もしくは、フリーズするようです。 では、PCの操作して下さい。できましたか? WKTVBDebuggerのウィンドウと、「VB Crack Me #2」のウィンドウが表示されます。 次に、ブレークポイントの設定にはいります。 「Form Manager (Ctrl+F)」を選択します。 Form Manager ウィンドウのコンボボックスの▼ボタンをクリックして、「frmMain」を選択します。 すると、「VB Crack Me #2」ウィンドウで使用されてるコントロールのボタンが有効になります。 「Command」ボタンをクリックします。 ウィンドウが、ポップアップします。 コンボボックスの▼ボタンをクリックすると、「cmdOK」と「cmdQuit」と表示されます。 「cmdOK」を選択します。 すると、いろいろ「cmdOK」ボタンに関する情報が表示されます。 ここで、「BPX」のボタンをクリックします。 これで、「登録」ボタン・クリックイベントの部分に、ブレークポイントが仕掛けられます。 もし邪魔であれば、「Form Maneger」のウィンドウを閉じて構いません。 (閉じるは、「×」ボタンクリック) 一応、ブレークポイントが有効か確認してみましょう。 「On Execution (Ctrl+E)」をクリックします。 「Current BPX and BPM List」ウィンドウが表示されます。 「004025F4」が緑色で表示されています。 0x004025F4でのブレークポイントが有効になってますね。 この「Current BPX and BPM List」ウィンドウも閉じて構いません。 さて、解析に入ります。 フェーク・シリアル「123」と入れて、「登録」ボタンをクリックします。 すると、0x004025F4でブレークし、P-Codeリストが表示されます。 4025F4: 1B LitStr: '不正シリアルです。' と表示されてます。 (Win9x系OSでは、文字化けしてると思います。) 2k・XP環境ならしっかり表示出来ていると思います。 P-Codeリストの 402607: 0d VCallHresult get__ipropTEXTEDIT この辺りで、入力シリアルの読込みをしていると思われます。 0x402607 のアドレスにBPを仕掛けて、動かす方法もありますが、 WKTVBDEの操作方法の学習も兼ねて、 「Trace X(F6)」を使います。 「Trace X(F6)」をクリックして、Lines to trace の入力ダイアログを表示されます。 アドレスではなく、実行する行数を入力するようです。 カレント行から7行目が、0x402607 ですので、「7」を入力してOKボタンクリックします。 そして、「Step Trace(F8)」で、2回ステップ実行してみましょう。 P-Codeリストの下側にある、ログ表示部分に FStStr -> '123' と表示されました。 おそらく、テキストボックス・コントロールから読込んだ、フェーク・シリアルです。 次に実行するP-Codeの「FStStr」ですが、先頭の「F」を除くと「Store String」の略ではないかと思われます。 代入先と思われる、0x001441DC の中身を見てみましょう。 (アドレス値は、スタック状態により毎回アドレスが変るので、同じではありません、WKTVBDEで表示されてるアドレスに読み替えてください。) 「Memory Dump(Ctrl+M)」をクリックします。 「Memory viewer and editor」というウィンドウが表示されます。 左上にある「Address to dump:」の所に、「14855C」と入力します。 0014855C: 31 00 32 00 33 00 00 00 1.2.3... と先頭に表示されます。 VBの内部では、UniCode形式で扱われているので1文字2バイトで表現されます。 UniCodeについて簡単に説明: 各国の文字を16ビット(2バイト)で扱い、他国語を1つのコードにまとめて処理しようとする為の文字コード体系 半角のASCII文字は、先頭バイトにASCIIコード、2バイトが、'0x00'となります。 よって、 0014855C: 31 00 32 00 33 00 00 00 を文字列で表現すると、'123’となります。 ここで、ExDecのP-Codeリストを見ると、 40260F: 31 FStStr local_0088 となってます。 local_0088 = 0x001441DC となるようですね。 「Step Trace(F8)」で、2回ステップ実行します。 402615: 6c ILdRf local_0088 と表示されてます。 「Step Trace(F8)」を1回押して下さい。 この時、スタック域にlocal_0088のアドレス(0x001441DC)が Push されます。 カーソル行の 402618: 1b LitStr: taty ですが、「taty」という文字列です。 気になる文字列ですね。 「Step Trace(F8)」を1回押して下さい。 また、スタック域にアドレスらしき値が Push されます。 そのアドレス値の中を見ると、 00401ED4: 74 00 61 00 74 00 79 00 t.a.t.y. と表示されます。 そして、 40261B: Lead0/3d NeStr です。 Lead0〜4について説明: 「Opcodes(Ctrl+O)」をクリックしてください。 「Opcodes Control Dialog」というウィンドウが表示されます。 このウィンドウには、「Generic Opcodes」・「Lead0」〜「Lead4」までのタブが表示されてます。 通常の場合は、「Generic Opcodes」のP-Codeを使用します。 「Lead n」のP-Codeを使用する場合は、「Generic Opcodes」の 0xFB から 0xFF のP-Codeを使用して、切替えを行います。 それでは、WKTVBDEのメイン・ウィンドウを見てみましょう。 P-Codeリストの下に、Op: FB 3D と表示されてます。 (実行するP-Codeです。) 1バイト目の、0xFB が、 Lead0の切替え命令です。 2バイト目の、0x3D が、NeStr命令です。 で、NeStrの動作ですが、直前にPushされてる、2つのアドレスが示す文字列の比較を行う命令です。 NeStrの場合、≠の時、True =の時、Falseを返します。 「Step Trace(F8)」を1回押して下さい。 スタック域に、0xffffffffが Pushされます。(NeStrの結果です。) '123' と 'taty' を比較してるので、結果は、True(0xffffffff)となります。 もし、結果が False の場合、0x00402673 へジャンプします。 ExDecのP-Codeリストで、0x00402673 を見ると、終了処理の方にジャンプしているようです。 おそらくダミー比較でしょう。(笑 0040261D: 1c BranchF: 00402673 (No Jump) なので、無視していきます。 次に、 0040262C: 10 ThisVCallHresult 0040278c にブレークポイントを仕掛けて見ましょう。 「On Execution (Ctrl+E)」をクリックします。 入力エリアに、「40262C」と入力して、「Add」ボタンをクリックします。 これでブレークポイント設定完了です。 「Go!(F5)」で一気に実行させます。 40262C でブレークします。 このコール先では何してるかは、後で解析することにして、Callを飛ばします。 「Trace Over(F10)」で、飛ばせます。 402631: 6c ILdRf 00149C9C の 00149C9C の中身を見て見ましょう。 00149C9C: 74 00 2E 00 41 00 2E 00 t...A... 00149CA4: 54 00 2E 00 75 00 00 00 T...u... と、文字列表現では、't.A.T.u'となります。 正解シリアル見たいですね。 402645: 5e ImpAdCallI4 kernel32!lstrcmpA ここで文字列比較してます。 402645 の 行をWクリックしてみてください。 ブレークポイントが設定されたと思います。 また、「Go!(F5)」で一気に実行させます。 ここで、スタック域にある、2つのアドレス値を見てみましょう。 1つ目は、0x149BF4 → '123' (フェークシリアル) 2つ目は、0x149BC4 → 't.A.T.u' (正解シリアル?) っと、ANSI形式で文字列が入ってます。 「Step Trace(F8)」を1回押して下さい。 スタック域に、0xffffffffが Pushされます。(lstrcmpAの結果と思われます。) 次に、 40265E: c7 EqI4 に、ブレークポイントを設定して、「Go!(F5)」で実行させます。 WKTVBDE の表示が、 40265E: c7 EqI4 0h,FFFFFFFFh ? となってますね。 P-Code「EqI4」は、Integer 4Byteの比較です。 FFFFFFFFhは、lstrcmpAの結果が、0であるかの比較ですね。 「EqI4」の結果が≠(False)の時、 40266A: 1c BranchF: 402673 でジャンプします。 =(True)の場合、ジャンプせずに、'正解です。'という文字列だ代入されるようです。 よって、't.A.T.u'が正解シリアルとなります。 ではここで、先程飛ばしたCall部分を解析してみましょう。 今まで設定した、ブレークポイントを全て解除します。 ここから先の部分は、非常に解りづらいので復習しましょう。 「On Execution (Ctrl+E)」を押して、Remove Allで解除されます。 次に、Callしてる所のアドレス、40262Cにブレークポイントを設定します。 それで、'taty'以外のシリアルを入力して、「登録」ボタンをクリックします。 何故、'taty'以外かと言うと、正解シリアル算出ロジックを通らないからです。 「登録」クリックイベント処理の先頭で止まりますね。 ここで、「Go!(F5)」で実行させます。 0040262C: 10 ThisVCallHresult 0040278c でブレークします。 「Step Trace(F8)」で、コール先に飛びます。 4026F0: Lead4/2d ZeroRetVal に来ます。 このP-Codeは、リターン値の0(ゼロ)クリアではないでしょうか。 「Step Trace(F8)」を、2回クリックします。 スタック域に、アドレスらしき値と1が Push されます。 カーソルがある行は、 4026FA: 0b ImpAdCallI2 rtcLeftCharBstr となってます。 これは、VBのLeft関数にあたります。 アドレス値の示す所を、見てみましょう。 001441DC: 74 00 61 00 74 00 79 00 t.a.t.y. っとなってます。 VBの表記にすると、「left$('taty', 1)」となり、最初の文字 't'が取り出されます。 「Step Trace(F8)」を、クリックします。 FStStrNoPop -> 't' とログ表示されますね。 この時の、スタック域に表示されてるアドレスの示す中身を見てみます。 0014931C: 74 00 00 00 xx xx xx xx t....... です。 「Step Trace(F8)」を、2回クリックします。 402705: 2a ConcatStr 0014931Ch 00402068h です。 Concat->'t' + '.' と表示されます。 (文字列の連結) 「Step Trace(F8)」を、2回クリックします。 402709: 28 LitVarI2: 0012F474h 1h, 1 にきます。 「Step Trace(F8)」を、クリックします。 0x000012F474の内容を見てみます。 0012F474: 02 00 00 00 00 00 00 00 0012F47C: 01 00 00 00 00 00 00 00 となってますねぇ〜 バリアント変数(VB内部使用)だと推測できます。 上位8バイトが、変数属性 下位8バイトが、変数値となっているようです。 整数型変数で、値が1となります。 「Step Trace(F8)」を、2回クリックします。 402716: 0b ImpAdCallI2 rtcMidCharBstr にカーソルがきています。 ここで、スタック域を見てみましょう。 はじめから、アドレス値、2(4 byte)となってます。 アドレス値の中身を見てみます。 001441DC: 74 00 61 00 74 00 79 00 t.a.t.y. VBのMID$関数のようですが、スタック域には2つしか引数が存在しません。 あとは、バリアント変数(VB内部使用)を使用していると思われます。 そこで、VB表記にすると、「mid$('taty', 2, 1)」となります。 「Step Trace(F8)」を、クリックします。 CVarStr -> 'a' と表示されます。 Mid$('taty',2,1)の結果です。 「Step Trace(F8)」を、2回クリックします。 402721: 0a ImpAdCallFPR4: rtcUpperCaseVar にカーソルがきています。 スタック域の値を見ます。 先頭のアドレスの方には、有効な値が入っていません。結果が入るエリアみたいですね。 2番目の方は、バリアント変数へのアドレスです。 0012F464: 08 00 00 00 00 00 00 00 0012F46C: 7C A1 14 00 00 00 00 00 8 文字列型 (String) の変数ですね。 さらに、0x14A17Cの中身を参照します。 0014A17C: 61 00 00 00 xx xx xx xx a....... 'a'が入ってますね。 「Step Trace(F8)」を、クリックします。 402726: 04 FLdRfVar 0012F454h と表示されてます。 0x0012F454も、バリアント変数へのアドレスです。 その内容は、'a'を大文字にした、'A'が入ってます。 「Step Trace(F8)」を、クリックします。 スタック域に2つのバリアント変数へのアドレスが入っています。 'A'と、't.'です。 結果のバリアント変数へのポインタがスタック域に残ります。 't.A'となります。 「Step Trace(F8)」を、2回クリックします。 バリアント変数 '.'と、't.A'の連結です。 「Step Trace(F8)」を、クリックします。 スタック域のアドレスが、結果バリアント変数へのアドレスです。 't.A.'となります。 402743: 0b ImpAdCallI2 rtcMidCharBstr まで実行させます。 これもまた、VB表記にすると「mid$('taty',3,1)」となります。 その後に、また大文字(rtcUpperCaseVar)に変換しているようですね。 402753: 04 FLdRfVar 0012F3D4h まで実行させます。 0x0012F3D4が、結果のバリアント変数へのアドレスです。 'T'が入ってます。 「Step Trace(F8)」を、クリックします。 バリアント変数 'T'と、't.A.'の連結です。 「Step Trace(F8)」を、2回クリックします。 バリアント変数 '.u'と、't.A.T'の連結です。 「Step Trace(F8)」を、クリックします。 スタック域のアドレスが、結果バリアント変数へのアドレスです。 't.A.T.u'となります。 これで、正解シリアルが作成できました。 VB表記にまとめと、 left$('taty', 1) + "." + UCase(mid$('taty', 2, 1)) + "." + UCase(mid$('taty', 3, 1)) + ".u" となります。 この部分は、かなり簡単に書いていますが、復習で何度もトレースしてみて下さい。 スペシャル講義として、ここからはダミー比較してる部分も正解シリアルにする方法を 簡単に講義します。 40261B: Lead0/3d NeStr にブレークポイントを設定します。 シリアル値 'taty'と入力して、「登録」ボタンをクリックしてください。 次に、「Go! (F5)」です。 ブレークポイントの部分で止まります。 「Step Trace(F8)」を1度行います。 40261D: 1c BranchF: 00402673 (Jump) となってます。 'taty'文字列と同じなので、NeStrでFalseが返るので、ジャンプします。 このジャンプ先を、正解処理の先頭に飛ばすようにすれば、'taty'も有効になります。 ジャンプ先は、 40266D: 1b LitStr です。 アドレスの計算を行うのですが、P-Codeのジャンプはコードの先頭からの相対オフセットとなります。 P-Codeの先頭は、0x4025F4 となります。 これは、ExDecリストの先頭のアドレスです。 よって、オフセット値は、 0x40266D - 0x4025F4 = 0x79 となります。 コードの書換えを行います。 「Edit」ボタンをクリックします。 先頭から、1C 7F 00 と表示されてます。 1C は、P-Codeです。 7F 00 がジャンプ先を示します。 (上位下位反転) 7F を 79 に変更すれば、いいようですね。 先頭の行を、Wクリックします。 ウィンドウがポップアップして、値の変更が可能になります。 7F を 79 に変更して、「Patch Now」ボタンをクリックします。 この時の、ファイルオフセット値は、File Offs: ******** とWKTVBDEに表示されています。 今回は、表示ファイル・オフセット+1の所になりますね。 ファイルオフセット値とは、バイナリエディタで、パッチを当てる時のオフセット値です。 pikさん所が参考になると思います。 http://doomo.org/kouza/add.html あとは、「Go! (F5)」で、実行されてみましょう。 あら、不思議・・・・、正解になりましたね。 余談ですが、 40266A: 1c BranchF: 402673 の飛び先換えても'taty'で NG かかるから無理ですが、 taty 以外なら一バイトで全てのパスが通るパッチは可能のようです。 二箇所・2byte ならそれも可能のようですね。 以上で、第1回目の講義終了になります。 長い間有り難うございます。 −特別付録・バリアント変数の属性コード− vbEmpty 0 Empty 値 (未初期化) vbNull 1 Null 値 (無効な値) vbInteger 2 整数型 (Integer) vbLong 3 長整数型 (Long) vbSingle 4 単精度浮動小数点数型 (Single) vbDouble 5 倍精度浮動小数点数型 (Double) vbCurrency 6 通貨型 (Currency) vbDate 7 日付型 (Date) vbString 8 文字列型 (String) vbObject 9 オブジェクト vbError 10 エラー値 vbBoolean 11 ブール型 (Boolean) vbVariant 12 バリアント型 (Variant) (バリアント型配列にのみ使用) vbDataObject 13 非OLE オートメーション オブジェクト vbDecimal 14 10 進数型 vbByte 17 バイト型 (Byte) vbUserDefinedType 36 ユーザー定義型を含むバリアント型 vbArray 8192 配列